package com.zhaungjie.base.utils.PropertiesCopyUtils.operation;

import com.zhaungjie.base.utils.LambdaStructureParser.LambdaStructureParser;
import com.zhaungjie.base.utils.PropertiesCopyUtils.PropertiesCopyConfig;
import com.zhaungjie.base.utils.PropertiesCopyUtils.PropertiesCopyConfigRoot;
import com.zhaungjie.base.utils.PropertiesCopyUtils.function.ProcessFunction;
import com.zhaungjie.base.utils.ReflectionUtils;

import java.lang.reflect.Constructor;
import java.util.*;

public class SimplePropertiesCopyOperation<S, T> {

    private PropertiesCopyConfig<S, T> propertiesCopyConfig;

    private PropertiesCopyConfigRoot<S, T> propertiesCopyConfigRoot;

    public SimplePropertiesCopyOperation(PropertiesCopyConfig<S, T> propertiesCopyConfig, PropertiesCopyConfigRoot<S, T> propertiesCopyConfigRoot) {
        this.propertiesCopyConfigRoot = propertiesCopyConfigRoot;
        this.propertiesCopyConfig = propertiesCopyConfig;
    }


    /**
     * 拷贝单个对象属性
     *
     * @param source 源对象
     * @param target 目标对象
     * @return {@link T}
     */
    private T propertiesCopy(S source, T target) throws InstantiationException, IllegalAccessException {
        if (source == null) return target;
        if (target == null) {
            try {
                // 获取目标类的无参构造方法
                Constructor<T> constructor = propertiesCopyConfigRoot.getTargetClazz().getDeclaredConstructor();
                // 设置构造方法可访问
                constructor.setAccessible(true);
                // 创建实例
                target = constructor.newInstance();
            }catch (Exception e){
                throw new RuntimeException("无法创建拷贝目标对象实例，请检查是否给目标对象加了无参构造方法,这是需要的。");
            }
        }

        Map<String, Object> kvMap = new HashMap<>();
        Map<String, S> keySourceMap = new HashMap<>();

        // 判断source是否为Map，如果不是将实例转为Map
        Map<String, Object> tempMap;
        if (source instanceof Map) {
            tempMap = (Map<String, Object>) source;
        } else {
            tempMap = ReflectionUtils.getObjectProperties(source);
        }

        for (String key : tempMap.keySet()) {
            keySourceMap.put(key, source);
        }
        kvMap.putAll(tempMap);

        Map<String, Object> targetMap = null;
        for (Map.Entry<String, Object> entry : kvMap.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            List<String> ignorePropertiesName = propertiesCopyConfig.getIgnorePropertiesName();
            Map<String, String> propertiesMap = propertiesCopyConfig.getPropertiesMap();
            // 如果在忽略名单-跳过
            if (ignorePropertiesName != null && ignorePropertiesName.contains(key)) continue;
            // 属性映射（映射后不再进行忽略名单的跳过，如果有问题，那就是使用的问题）
            if (propertiesMap != null) {
                String mapKey = propertiesMap.get(key);
                key = mapKey != null ? mapKey : key;
            }
            // 看是否存在processMap
            if (propertiesCopyConfig.getProcessMap() != null) {
                // `source的key`的值处理程序
                ProcessFunction<S> processFunction = propertiesCopyConfig.getProcessMap().get(key);
                if (processFunction != null) {
                    // 之前存也卡key与source，所以下面source一定有值
                    value = processFunction.get(keySourceMap.get(key));
                }
            }
            // 如果源的属性为空就忽略该项
            if (value == null) {
                continue;
            }
            // 看target是否为Map，如果是直接put，如果不是需要使用反射方法setFieldValue将属性值设置到对应的属性上
            if (target instanceof Map) {
                if (targetMap == null) {
                    targetMap = (Map<String, Object>) target;
                }
                targetMap.put(key, value);
            } else {
                try {
                    ReflectionUtils.setFieldValue(target, key, value);
                } catch (Exception e) {
                    // 如果设置出错了，忽略，因为如果使用key获取不到对应的属性也会报错，所以忽略就好
                }
            }
        }
        return target;
    }

    /**
     * 拷贝属性 （兼容拷贝原/目标对象为Map）
     *
     * @param sources 多个源对象
     * @param target  目标
     * @return {@link T}
     */
    private T propertiesCopy(S[] sources, T target) throws InstantiationException, IllegalAccessException {
        if (sources == null || sources.length == 0) return target;

        // 依赖新扩展的方法
        for (S source : sources) {
            target = propertiesCopy(source, target);
        }

        return target;
    }

    private void setTargetTemplate(T targetTemplate) {
        this.propertiesCopyConfigRoot.setTemplate(targetTemplate);
    }

    private T getTargetTemplate() {
        return this.propertiesCopyConfigRoot.getTemplate();
    }

    public SimplePropertiesCopyOperation template(T targetTemplate) {
        setTargetTemplate(targetTemplate);
        return this;
    }

    public T produce(S[] sources) {
        try {
            return propertiesCopy(sources, getTargetTemplate());
        } catch (Exception e) {
            throw new RuntimeException("PropertiesCopy工具: 拷贝出现问题，原因 "+e.getMessage());
        }
    }

    public T produce(S source) {
        return produce((S[])new Object[]{source});
    }

    /**
     * 批量生产(拷贝)
     *
     * @param sources 来源
     * @return {@link List}<{@link T}>
     */
    public List<T> batchProduce(List<S> sources){
        List<T> targets = new ArrayList<>();
        try {
            for (S source : sources) {
                targets.add( propertiesCopy(source, getTargetTemplate()));
            }
            return targets;
        }catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("PropertiesCopy工具: 批量拷贝出现问题，原因 "+e.getMessage());
        }
    }


    /**
     * 关联开始 - keyCreate
     * @param sourceSFunction
     * @param targetSFunction
     * @return
     */
    public AssociatedCopyOperation<S,T> associated(LambdaStructureParser.NANRFunction<S> sourceSFunction, LambdaStructureParser.NANRFunction<T> targetSFunction) {
        return new AssociatedCopyOperation<>(propertiesCopyConfigRoot, sourceSFunction,targetSFunction);
    }
}